package com.gsbelarus.gedemin.salary.util;

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;

import com.afollestad.materialdialogs.MaterialDialog;
import com.gsbelarus.gedemin.salary.R;
import com.gsbelarus.gedemin.salary.database.RealmHelper;
import com.gsbelarus.gedemin.salary.entity.model.ExRatesMonthlyModel;

import java.lang.ref.WeakReference;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Executors;

import io.realm.Realm;

public class CurrencyHelper {

    private static final String RATE_ON_ABBREVIATION_XPATH = "//NewDataSet/DailyExRatesOnDate[Cur_Abbreviation=\"%s\"]/Cur_OfficialRate";

    private static final String URL = "http://www.nbrb.by/Services/ExRates.asmx";
    private static final String SOAP_ACTION = "http://www.nbrb.by/ExRatesDaily";
    private static final String NAMESPACE = "http://www.nbrb.by/";
    private static final String METHOD_NAME = "ExRatesDaily";
    private static final String PROPERTY = "<onDate>%s</onDate>";

    private static final String FORMAT_DOUBLE_SUM = "#,###,###.##";
    public static final String FORMAT_LONG_SUM = "#,###,###";
    private static final String SWITCH_MODE = "switch_mode";

    private Context context;
    private static CurrencyHelper instance;
    private DecimalFormat formatter;
    private MaterialDialog dialog;
    private List<OnCurrencySwitcherChanged> listListeners;
    private WeakReference<Activity> lastOwnerActivity = new WeakReference<>(null);
    private Handler handler;
    private volatile boolean isSuccessfulUpdating = true;
    private volatile boolean isUpdating = false;

    public enum Kind {
        BYR, USD, EUR
    }

    private Kind lastChoice;

    private MaterialDialog.ButtonCallback callBack = new MaterialDialog.ButtonCallback() {
        @Override
        public void onPositive(final MaterialDialog dialog) {
            super.onPositive(dialog);
            updatesRates(null);
        }
    };

    public interface OnCurrencySwitcherChanged {
        void onChanged(Kind kind);
    }

    public interface OnFinishUpdateListener {
        void onFinish(boolean isSuccessfulUpdating);
    }

    private CurrencyHelper(Context context) {
        this.context = context;
        lastChoice = getChoice();
        listListeners = new ArrayList<>();
        handler = new Handler();

        formatter = new DecimalFormat();
        DecimalFormatSymbols custom = new DecimalFormatSymbols();
        custom.setGroupingSeparator(' ');
        custom.setDecimalSeparator('.');
        formatter.setDecimalFormatSymbols(custom);
        changeFormatterPattern();
    }

    public synchronized static CurrencyHelper getInstance(Context context) {
        if (instance == null)
            instance = new CurrencyHelper(context);
        return instance;
    }

    public void onStartUpdateDB() {
        isUpdating = true;
        if (dialog != null && dialog.isShowing()) {
            dialog.cancel();
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Activity activity = lastOwnerActivity.get();
                    if (activity != null && !activity.isFinishing())
                        showDownloadDialog(activity);
                }
            });
        }
    }

    public void onFinishUpdateDB() {
        isUpdating = false;
        if (dialog != null && dialog.isShowing()) {
            dialog.cancel();
            handler.post(new Runnable() {
                @Override
                public void run() {
                    Activity activity = lastOwnerActivity.get();
                    if (activity != null && !activity.isFinishing()) {
                        if (isSuccessfulUpdating)
                            showChoiceDialog(activity, null);
                        else
                            showErrorDialog(activity, R.string.dialog_rates_text_error, callBack);
                    }
                }
            });
        }
    }

    public void updatesRates(final OnFinishUpdateListener onFinishUpdateListener) {
        onStartUpdateDB();
        Executors.newFixedThreadPool(1).execute(new Runnable() {
            @Override
            public void run() {
                RealmHelper realmHelper = new RealmHelper(Realm.getInstance(context));
                final boolean isSuccessfulUpdating = updatesRatesToBackend(realmHelper, true);
                realmHelper.getRealm().close();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        onFinishUpdateDB();
                        if (onFinishUpdateListener != null)
                            onFinishUpdateListener.onFinish(isSuccessfulUpdating);
                    }
                });
            }
        });
    }

    /**
     * Пример использования:<br>
     * {@link #onStartUpdateDB()} <br>
     * {@link #updatesRatesToBackend(RealmHelper, boolean)}<br>
     * {@link #onFinishUpdateDB()}
     *
     * @return true - успешно, false - неудачно
     */
    public boolean updatesRatesToBackend(RealmHelper realmHelper, boolean withTransaction) {
        try {
            isSuccessfulUpdating = true;

            List<Calendar> posDates = realmHelper.getPayslipPossibleDatesList();

            for (Calendar cal : posDates) {
                if (realmHelper.getExRatesMonthly(cal) == null) {

                    String dateString = SOAPWebServiceManager.soapFormatDate(cal.getTime());
                    String requestEnvelopeProperty = String.format(PROPERTY, dateString);
                    String result = SOAPWebServiceManager.call(URL, SOAP_ACTION, METHOD_NAME, NAMESPACE, requestEnvelopeProperty);

                    if (result == null) throw new Exception();
                    ExRatesMonthlyModel ratesMonthlyModel = ModelFactory.newInstanceExRatesMonthlyModel(
                            new ExRatesMonthlyModel(),
                            cal.getTime(),
                            Double.parseDouble(SOAPWebServiceManager.parseResponse(String.format(RATE_ON_ABBREVIATION_XPATH, "USD"), result)),
                            Double.parseDouble(SOAPWebServiceManager.parseResponse(String.format(RATE_ON_ABBREVIATION_XPATH, "EUR"), result)));

                    realmHelper.save(ratesMonthlyModel, withTransaction);
                }
            }

            Log.i(CurrencyHelper.class.getSimpleName(), "Курсы валют обновлены");
        } catch (Exception e) {
            //Crashlytics.getInstance().core.logException(e);
            isSuccessfulUpdating = false;
            setChoice(Kind.BYR);
            Log.i(CurrencyHelper.class.getSimpleName(), "Курсы валют не обновлены");
        }
        return isSuccessfulUpdating;
    }

    public void showChoiceDialog(final Activity activity, final OnCurrencySwitcherChanged onCurrencySwitcherChanged) {
        lastOwnerActivity = new WeakReference<>(activity);

        if (activity != null && !activity.isFinishing()) {

            if (!isSuccessfulUpdating && !isUpdating) {
                showErrorDialog(activity, R.string.dialog_rates_text_error, callBack);
                return;
            }

            if (isUpdating) {
                showDownloadDialog(activity);
                return;
            }

            MaterialDialog.Builder dialogBuilder = new MaterialDialog.Builder(activity);
            dialogBuilder
                    .title(R.string.action_currency_switch)
                    .negativeText(R.string.cancel_text)
                    .items(R.array.currency_list)
                    .itemsCallbackSingleChoice(getLastChoice().ordinal(), new MaterialDialog.ListCallbackSingleChoice() {
                        @Override
                        public boolean onSelection(MaterialDialog materialDialog, View view, int i, CharSequence charSequence) {
                            Kind kind = Kind.values()[i];
                            setChoice(kind);
                            if (onCurrencySwitcherChanged != null)
                                onCurrencySwitcherChanged.onChanged(kind);
                            return false;
                        }
                    });

            dialog = dialogBuilder.build();
            dialog.show();
        }
    }

    private void showErrorDialog(final Activity activity, int content, MaterialDialog.ButtonCallback callback) {
        dialog = new MaterialDialog.Builder(activity)
                .title(R.string.action_currency_switch)
                .content(content)
                .positiveText(R.string.action_repeat)
                .negativeText(R.string.action_hide)
                .callback(callback)
                .build();
        dialog.show();
    }

    private void showDownloadDialog(Activity activity) {
        dialog = new MaterialDialog.Builder(activity)
                .title(R.string.action_currency_switch)
                .content(R.string.dialog_rates_text_download)
                .progress(true, 0)
                .build();
        dialog.show();
    }

    public void setChoice(Kind choice) {
        if (lastChoice != choice) {
            this.lastChoice = choice;
            changeFormatterPattern();
            saveChoice(choice);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    for (OnCurrencySwitcherChanged listener : listListeners)
                        listener.onChanged(lastChoice);
                }
            });
        }
    }

    public double getConvertedSum(RealmHelper realmHelper, Calendar date, double sum) {
        return (sum / getRate(realmHelper, date));
    }

    public String getConvertedSumString(RealmHelper realmHelper, Calendar date, double sum) {
        return formatter.format((float) (sum / getRate(realmHelper, date)));
    }

    public String getKindOfCurrencyText(String firstText) {
        switch (lastChoice) {
            case BYR:
                return firstText + context.getString(R.string.rub);
            case USD:
                return firstText + context.getString(R.string.usd);
            case EUR:
                return firstText + context.getString(R.string.eur);
            default:
                return null;
        }
    }

    public DecimalFormat getFormatter() {
        return formatter;
    }

    public Kind getLastChoice() {
        return lastChoice;
    }

    private double getRate(RealmHelper realmHelper, Calendar date) {
        if (lastChoice == Kind.BYR) return 1;

        double rateDouble = 1;

        ExRatesMonthlyModel rate = realmHelper.getExRatesMonthly(date);

        if (rate != null) {
            switch (lastChoice) {
                case USD:
                    rateDouble = rate.getUsdRate();
                    break;
                case EUR:
                    rateDouble = rate.getEurRate();
                    break;
            }
        }

        return rateDouble;
    }

    private void changeFormatterPattern() {
        if (lastChoice == Kind.BYR)
            formatter.applyPattern(FORMAT_LONG_SUM);
        else
            formatter.applyPattern(FORMAT_DOUBLE_SUM);
    }

    private void saveChoice(Kind choice) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = prefs.edit();

        editor.putInt(SWITCH_MODE, choice.ordinal());
        editor.commit();
    }

    private Kind getChoice() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        return Kind.values()[prefs.getInt(SWITCH_MODE, Kind.BYR.ordinal())];
    }

    public void addOnCurrencySwitcherChanged(OnCurrencySwitcherChanged onCurrencySwitcherChanged) {
        if (!listListeners.contains(onCurrencySwitcherChanged))
            listListeners.add(onCurrencySwitcherChanged);
    }

    public void removeOnCurrencySwitcherChanged(OnCurrencySwitcherChanged onCurrencySwitcherChanged) {
        listListeners.remove(onCurrencySwitcherChanged);
    }
}
